import java.io.*;
import java.net.*;

/** A client used to connect to other routers and communicate routing tables. */
public class newClient implements Runnable{
	/** Socked used to connect from this client to another router's server. */
	private Socket echoSocket = null;
	/** Use this to write information to the other router. */
    public DataOutputStream  out = null;
    /** Use this to read information from the other router. */
    public DataInputStream in = null;
    boolean isConnected = false;
    String message = null;
    /** The port on which the server we want to connect is listening. */
    int port;
    /** The ip of the server to which we want to connect. */
    String ip;
	
    public newClient(String otherIP, int otherPORT){
    	this.ip = otherIP;
    	this.port = otherPORT;
    }
    
	public void run() {
		System.out.println("Client: " + Thread.currentThread() + " " + this.ip + " " + this.port);
		this.tryAgain();
	}
	
	/**
	 * Simply connect to the other router using a new Socket object and
	 * resolving the ip or hostname.
	 * @throws UnknownHostException
	 * @throws IOException
	 */
	public void connectToServer() throws UnknownHostException, IOException{
		System.out.println("Trying to connect...");
		this.echoSocket = new Socket(InetAddress.getByName(this.ip),this.port);
		this.initiateStreams();
		this.establishConnection();
		System.out.println("Connected to: "+echoSocket.getInetAddress());
	}
	
	/**
	 * Get the associated input and output streams from the socket
	 * so we can use them to communicate between this router and
	 * the other.
	 * @throws IOException
	 */
	public void initiateStreams() throws IOException{
		this.out = new DataOutputStream(echoSocket.getOutputStream());
    	this.out.flush();
    	this.in = new DataInputStream(echoSocket.getInputStream());
    	System.out.println("Got Client I/O streams");
	}
	
	/**
	 * The client initiates the disconnection procedure.
	 * So we send the TERMINATE signal to the other router
	 * and clean up our sockets and streams.
	 */
	public void closeConnection() {
		try {
			if (this.out != null) {
				this.out.writeUTF("TERMINATE");
				this.out.flush();
				this.out.close();
			}
			if (this.in != null) this.in.close();
			if (this.echoSocket != null) this.echoSocket.close();
			this.isConnected = false;
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}
	
	/** Loop till we get an actual response from the server. */
	public void establishConnection(){
		
		do
		{
			try {
				this.out.writeUTF("Connected");
				} 
			catch (IOException e) {
					e.printStackTrace();
					}
			this.message = this.listen();
			this.isConnected = true;
		}
        while(this.message != "TERMINATE" && this.isConnected == false);
		
		System.out.println("message from server: " + this.message);
	}
	
	/**
	 * Helper method so we can send data from the interface.
	 * @param msg What we want to send to another router.
	 */
	public void send(String mesg) {
		try {
			this.out.writeUTF(mesg);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Helper method so we can receive data from the interface.
	 * We first check if there is more than one byte to read.
	 * @return msg What was read from another router. Otherwise null.
	 */
	public String listen() {
		String msg = null;
		try {
			while (msg == null) {
				msg = this.in.readUTF();
				try { Thread.sleep(50); } catch (InterruptedException e) {e.printStackTrace();}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return msg;
	}
	
	public void tryAgain(){
		
		try {
			this.connectToServer();
			this.initiateStreams();
			this.establishConnection();
		} catch (EOFException e) {
			System.err.println("Client Terminated");
    	
        } catch (IOException e) {
        	//e.printStackTrace();
        	System.err.println("\nCouldn't get I/O for "
                           + "the connection to: "+this.ip+".");
        	try {
				Thread.sleep(3000);
				this.tryAgain();
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
        //finally {
        //	this.closeConnection();
        //}
	}
}